from system.object import Object
from system.eventServer import eventServer
from system.gameServer import gameServer
from system.timer import Timer
from system.vec2 import Vec2
from system.dynload import dynload

import random
import math

class CalmServer(Object):
    def __init__(self, parent, type = None, name = None):
        Object.__init__(self, parent, type, name)
        
        #self.addChild("calm.loopController")
        #eventServer.pull("initLoopController",{'ind':[0 for i in range(64)]})
        
        self.defaultVar("numAgents",10)
        self.defaultVar("numEvals",1)
        self.defaultVar("minIndEvals",1)
        self.defaultVar("evalPop",self.getVar("numAgents"))
        self.defaultVar("runTime",2)
        
        self.defaultVar("sensors",{})
        self.defaultVar("actuators",{})
        self.defaultVar("evaluators",{})
        
        self.defaultVar("ga",{})
        self.defaultVar("agent",{})
        self.defaultVar("controller",{})
        
        self.makeEvaluators()
        
        numAgents = self.getVar("numAgents")
        self.agents = []
        for i in range(numAgents):
            agent = self.addChild("calm.agent")
            agent.setVar("pos",(gameServer.screenw/2,gameServer.screenh/2))
            agent.setVar("rot",0)
            agent.controller.vars.update(self.getVar("controller"))
            agent.vars.update(self.getVar("agent"))
            self.setupController(agent.controller)
            agent.controller.calcNumBits()
            genomeSize = agent.controller.numBits
            self.agents.append(agent)
            
        print "evolving %d bits" % genomeSize
            
        self.evalTimer = None
        self.pop = []
        self.gen = 0
        
        eventServer.register("evaluateIndividual",self.evaluateIndividual)
        eventServer.register("update",self.update)
            
        self.ga = self.addChild("ai.ga")
        gaDict = self.getVar("ga")
        gaDict.update({"genomeSize":genomeSize,'popSize':numAgents,'path':self.ga.getVar("path")})
        eventServer.pull("startEvolution",gaDict)
        
    def evaluateIndividual(self, cmd, args):
        self.pop.append(args['ind'])
        if len(self.pop) == len(self.agents):
            self.startGeneration()
            
    def startGeneration(self):
        #print "start gen", self.gen
        
        #self.fitness = dict([(i,0) for i in range(len(self.agents))])
        self.fitness = dict([(i,[]) for i in range(len(self.agents))])
        self.intFitness = dict([(i,0) for i in range(len(self.agents))])
        self.eval = 0
        
        self.startEval()
        
    def startEval(self):
        #print "start eval", self.eval
        self.evalTimer = Timer(self.getVar("runTime"))
        
        evalPop = self.getVar("evalPop")
        evalPosList = random.sample(range(len(self.agents)),evalPop)
        self.evalMap = dict([(i,evalPosList[i]) for i in range(evalPop)])
        
        #print "using agents", evalPosList
        
        for agent in self.agents:
            agent.controller.transition("idle")
            agent.setVar("shown",False)
        
        #for ind, agent in zip(self.pop,self.agents):
        for i in self.evalMap.itervalues():
            ind = self.pop[i]
            agent = self.agents[i]
            controller = agent.controller
            
            agent.setVar("shown",True)
            
            self.setupController(controller)
            self.setupAgent(agent)
            
            path = controller.getVar("path")
            eventServer.pull("initLoopController",{"path":path,'ind':ind})
            
    def update(self, cmd, args):
        if self.evalTimer:
            dtime = args['dtime']
            
            if self.evalTimer.fire():
                self.evalTimer = None
                self.finishEval()
                
            #for i, agent in enumerate(self.agents):
                #dfitness = 0
                #for evaluator in self.evaluators.itervalues():
                    #dfitness += evaluator.evaluate(agent)
                #self.fitness[i] += dfitness * dtime
                
            for i in self.evalMap.itervalues():
                dfitness = 0
                for evaluator in self.evaluators.itervalues():
                    dfitness += evaluator.evaluate(self.agents[i])
                self.intFitness[i] += dfitness * dtime
                
    def finishEval(self):
        #print "finish eval", self.eval
        self.eval += 1
        
        for i in self.evalMap.itervalues():
            for evaluator in self.evaluators.itervalues():
                #self.fitness[i] += evaluator.endEvaluate(self.agents[i])
                self.fitness[i].append(evaluator.endEvaluate(self.agents[i]) + self.intFitness[i])
                self.intFitness[i] = 0
                
        indEvals = self.getVar("minIndEvals")
        allEvals = all(map(lambda x: len(x) >= indEvals, self.fitness.values()))
        
        if self.eval < self.getVar("numEvals") or not allEvals:
            self.startEval()
        else:
            self.finishGeneration()
            
    def finishGeneration(self):
        #print "finish gen", self.gen
        
        #fitness = {}
        #for i in range(len(self.agents)):
            #fitness[i] = sum(self.fitness[i]) / float(len(self.fitness[i]))
            
        fitness = dict([(i,sum(self.fitness[i]) / float(len(self.fitness[i]))) for i in range(len(self.agents))])
        
        maxFitness = max(fitness.values())
        minFitness = min(fitness.values())
        aveFitness = sum(fitness.values()) / float(len(fitness.values()))
        #print self.fitness.values()
        #print fitness.values()
        print self.gen, maxFitness, minFitness, aveFitness
        
        self.gen += 1
        self.pop = []
        
        for i, ifitness in fitness.iteritems():
            eventServer.pull('returnFitness',{'id':i,'fitness':ifitness})
            
    def setupAgent(self, agent):
        
        screenw = gameServer.screenw
        screenh = gameServer.screenh
        
        agent.setVar("pos",(screenw/2,screenh/2))
        agent.setVar("rot",math.pi)
        
        #agent.setVar("pos",(random.uniform(screenw*0.25,screenw*0.75),random.uniform(screenh*0.25,screenh*0.75)))
        #agent.setVar("rot",random.uniform(0,math.pi*2))
            
    def setupController(self, controller):
        controller.clearSensors()
        controller.clearActuators()
        
        for name, vars in self.getVar("sensors").iteritems():
            if vars.has_key("type"):
                type = vars['type']
            else:
                type = name.split('_')[0]
            controller.addSensor(type,name,vars)
            
        for name, vars in self.getVar("actuators").iteritems():
            if vars.has_key("type"):
                type = vars['type']
            else:
                type = name.split('_')[0]
            controller.addActuator(type,name,vars)
            
    def makeEvaluators(self):
        self.evaluators = {}
        
        for name, vars in self.getVar("evaluators").iteritems():
            if vars.has_key("type"):
                type = vars['type']
            else:
                type = name.split('_')[0]
                
            evaluatorClass = dynload(type)
            assert evaluatorClass
            
            evaluator = evaluatorClass(self,vars)
            self.evaluators[name] = evaluator
